class Vue {
  constructor ($options) {
    this.$data = $options.data
    // 调用数据劫持的方法
    Observe(this.$data)
    
    // 属性代理 给vm代理this.$data的属性
    Object.keys(this.$data).forEach(key => {
      Object.defineProperty(this,key, {
        writable: true, //可修改
        enumerable: true,   // 控制属性是否可枚举，能不能被循环获取
        configurable: true, // 可配置
        get() {
          return this.$data[key]
        },
        set (newVal) {
          this.$data[key] = newVal
        }
      })
    })

    // 调用模板编译的方法
    Compile($options.el,this) //this是当前实例,$options.el结构，this数据
  }
}

// 数据劫持的方法
function Observe (obj) {
  if (!obj || typeof obj !== 'object') return

  // 通过 Object.keys(obj) 获取到当前 obj 上的每个属性
  Object.keys(obj).forEach(key => {
    // 当前被循环的 key 所对应的属性值
    let value = obj[key]
    // 如果下面还是对象调用
    Observe(value)
    Object.defineProperty(obj,key, {
      enumerable: true,
      configurable: true,
      get () {
        // 返回对应key的值
        return value
      },
      set (newVal) {
        // 重新赋值
        value = newVal
        Observe(value) // 对象下面的对象
      }
    })
  })
}

// 对HTML结构进行模板编译的方法
function Compile (el,vm) {
  // 获取el对应的DOM元素
  vm.$el = document.querySelector(el)
  // 创建文档碎片,提高DOM操作的性能
  const fragment =  document.createDocumentFragment()
  // 把el里面的元素放入fragment
  while (childNode = vm.$el.firstChild) {
    // 把第一个元素放入
    fragment.appendChild(childNode)
  }
  //进行模板编译
  replace(fragment)

  // 把fragment重新渲染给el
  vm.$el.appendChild(fragment)


  // 对DOM模板进行编译的方法
  function replace (node) {
    // 定义匹配插值表达式的正则 处理模板编译里面过滤空格 {{ age }}
    const regMustache = /\{\{\s*(\S+)\s*\}\}/

    // 证明当前的node节点是一个文本子节点，需要进行正则的替换,终止递归
    if (node.nodeType === 3) {
      //注意： 文本子节点也是一个DOM对象，如果要获取文本子节点的字符串内容，需要调用textContent属性获取
      const text = node.textContent
      // console.log(node.textContent)
      // 进行字符串的正则匹配与提取
      const execResult = regMustache.exec(text)
      // console.log(execResult)
      if (execResult) {
        // node.textContent = 'aaa'
        // 获取到对应的对象属性值
        const value = execResult[1].split('.').reduce((newObj,k) => newObj[k], vm)
        // console.log(value)
        // 替换对应的属性值
        node.textContent = text.replace(regMustache,value)
      }
      // 终止递归的条件
      return 
    }
    // 证明不是文本节点，可能是一个DOM元素，需要进行递归处理
    node.childNodes.forEach(child => replace(child))
  }
}